package com.eureka.provider.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eureka.provider.pojo.Order;
import com.eureka.provider.pojo.SeckillOrder;
import com.eureka.provider.pojo.User;
import com.eureka.provider.service.IGoodsService;
import com.eureka.provider.service.IOrderService;
import com.eureka.provider.service.ISeckillOrderService;
import com.eureka.provider.service.IUserService;
import com.eureka.provider.vo.GoodsVo;
import com.eureka.provider.vo.RespBean;
import com.eureka.provider.vo.RespBeanEnum;
import com.netflix.discovery.converters.Auto;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @author sam
 * @since 2021-04-15
 */
@RestController
@RequestMapping("/seckill_goods")
@Slf4j
public class SeckillGoodsController {
    @Autowired
    IGoodsService goodsService;
    @Autowired
    ISeckillOrderService seckillOrderService;
    @Autowired
    IOrderService orderService;
    @Autowired
    IUserService userService;

    /**
     * 秒杀1：程序锁(AOP锁)
     * QPS 17
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/doSecKill1")
    public RespBean doSecKill1(User user, Long goodsId) throws Exception {
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKillSync(user,goodsId);
    }

    /**
     * 秒杀2：数据库悲观锁
     * QPS 42
     * @param user
     * @param goodsId
     * @return
     */

    @RequestMapping("/doSecKill2")
    public RespBean doSecKill2(User user, Long goodsId){
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKill2(user,goodsId);
    }

    /**
     * 秒杀3：数据库乐观锁
     * QPS 26
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/doSecKill3")
    public RespBean doSecKill3(User user, Long goodsId){
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKill3(user,goodsId);
    }

    /**
     * 秒杀4：阻塞队列异步下单
     * QPS 40
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/doSecKill4")
    public RespBean doSecKill4(User user, Long goodsId){
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKill4(user,goodsId);
    }

    /**
     * 秒杀5：redis缓存优化+异步下单
     * QPS 1071
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/doSecKill5")
    public RespBean doSecKill5(User user, Long goodsId){
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKill5(user,goodsId);
    }

    /**
     * 秒杀6：redis缓存+同步下单
     * @param user
     * @param goodsId
     * @return
     */

    @RequestMapping("/doSecKill6")
    public RespBean doSecKill6(User user, Long goodsId){
        if(user==null){
            return RespBean.Error(RespBeanEnum.NOT_LOGIN);
        }
        return goodsService.doSecKill6(user,goodsId);
    }

    @Autowired
    RedisTemplate redisTemplate;
    @RequestMapping("/deduce_stock")
    public void deduce_stock(){
        /**
         * 1.超时时间小于业务执行时间，锁被其他线程删除
         * 2.业务出现bug，没有释放锁
         * 3.过期时间设置不好把握
         */
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "lock",10, TimeUnit.SECONDS);
        /*
        可能出现死锁
         */
        try {
            if(!lock) {
                return;
            }
            int stock=(int)redisTemplate.opsForValue().get("stock");
            if(stock>0){
                redisTemplate.opsForValue().set("stock",stock-1);
                log.info("减库存成功");
            }else{
                log.info("失败");
            }
        }finally {
            redisTemplate.delete("lock");
        }


    }

    @Autowired
    Redisson redisson;
    @RequestMapping("/deduce_redisson")
    public void deduce_redisson(){
        /**
         * redis主从架构锁失效问题：
         * CAP（一致性、可用性、分区容错性）理论：redis集群满足AP 单机CP zk集群满足CP
         * 原因：redis集群加锁后马上返回客户端结果，然后同步（牺牲一致性）
         *     zk集群先保证半数节点同步成功，再返回加锁结果（牺牲可用性）
         * 选择策略：redis并发高 zk并发较低
         */
        RLock lock = redisson.getLock("product");
        try {
            //如果加锁失败，while循环自旋加锁
            lock.lock();
            int stock=(int)redisTemplate.opsForValue().get("stock");
            if(stock>0){
                redisTemplate.opsForValue().set("stock",stock-1);
                log.info("减库存成功");
            }else{
                //log.info("失败");
            }
        }finally {
            lock.unlock();
        }
    }
}
